package com.anupam;

import java.util.List;

import com.google.common.base.Preconditions;

/**
 * A class representing a M*N Matrix representation of Grid, it's not Thread Safe.
 *
 * @author anupamsaini@gmail.com (Anupam Saini)
 *
 */
public class Grid {

  /**
   * Size of the the grid along X axis.
   */
  private final int xAxisSize;

  public int getxAxisSize() {
    return xAxisSize;
  }

  /**
   * Size of the grid along Y axis.
   */
  private final int yAxisSize;

  public int getyAxisSize() {
    return yAxisSize;
  }

  /**
   * A M*N matrix representing a Grid of {@link Cell} type.
   */
  private Cell grid[][] = null;

  public Cell[][] getGrid() {
    return grid;
  }

  /**
   * Constructor used for associating state information like X and Y axis size.
   *
   * @param xAxisSize
   * @param yAxisSize
   */
  public Grid(int xAxisSize, int yAxisSize) {
    this.xAxisSize = xAxisSize;
    this.yAxisSize = yAxisSize;
  }

  /**
   * Generates a Grid object on the basis of input json.
   *
   * @param xAxisSize
   * @param yAxisSize
   */
  public Cell[][] generateGrid(List<Integer> state) {

    Preconditions.checkArgument((this.xAxisSize > 1) || (this.yAxisSize > 1),
        " The X and Y dimensions must be greater than 1");
    Preconditions.checkArgument((this.xAxisSize * this.yAxisSize) == state.size(),
        "The size of the state array must match the product of dimensions");
    this.grid = new Cell[this.xAxisSize][this.yAxisSize];

    for (int i = 0; i < this.xAxisSize; i++) {
      for (int j = 0; j < this.yAxisSize; j++) {
        /*
         * We always need to remove the value from 0th index as precondition checks have already
         * determined the correctness of state List.
         */
        int stateValue = state.remove(0);

        Preconditions.checkArgument((stateValue == 0 || stateValue == 1),
            "Invalid input, The state value can be either 0 or 1");

        if (stateValue == 1) {
          this.grid[i][j] = new Cell(true);
        } else {
          this.grid[i][j] = new Cell(false);
        }
      }
    }
    return this.grid;
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < this.xAxisSize; i++) {
      sb.append("[");
      for (int j = 0; j < this.yAxisSize; j++) {
        if(this.grid[i][j].isAlive()){
          sb.append("x").append(",");
        }else{
          sb.append("-").append(",");
        }
      }
      sb.append("]").append("\n");
    }
    return sb.toString();
  }

  public final class Cell {
    /**
     * Indicates the state of a cell, true indicates alive and false indicates a dead cell.
     */
    private boolean state;

    private int liveNeighbours = 0;

    public Cell(boolean state) {
      this.state = state;
    }

    /**
     * Returns the current cell state.
     *
     * @return the state
     */
    public boolean isAlive() {
      return state;
    }

    /**
     * Sets a particular cells state.
     *
     * @param state .
     */
    public void setState(boolean state) {
      this.state = state;
    }

    public void incLiveNeighbours() {
      this.liveNeighbours = this.liveNeighbours + 1;
    }

    public int getLiveNeighbours() {
      return this.liveNeighbours;
    }
  }
}
